package org.pushforward.iwillsurvive.helpers;

import java.io.EOFException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

import javax.servlet.http.HttpServletResponse;

/**
 * @author Sabre Runner
 */
public class PageBuilder 
{
	static public String indexHereLink = "<a href=\"./index.html\">here</a>";

	HttpServletResponse response;
	StringBuilder headBuilder = new StringBuilder();
	StringBuilder bodyBuilder = new StringBuilder();
	String bodyIndent = "\t\t";
	String bodyClose = "\t</body>\n</html>";
	boolean headClosed = false;
	boolean bodyClosed = false;
	static String headclosedString = "The head has already been closed.\n" +
									 "No further head additions possible.\n" +
									 "Feel free to continue adding to the body.\n";
	static String bodyClosedString = "The page has already been closed.\n" + 
									 "No further additions possible.";

	public PageBuilder(HttpServletResponse response) 
	{
		this.response = response;
		headBuilder
				.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\""
						+ "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
						+ "<html>\n" + "\t<head>\n");
		bodyBuilder.append("\t<body>\n");
	}

	private void CheckHeader() throws EOFException
	{
		if (headClosed)
		{
			throw new EOFException(headclosedString);
		}
	}

	private void CheckBody() throws EOFException
	{
		if (bodyClosed)
		{
			throw new java.io.EOFException(bodyClosedString);
		}
	}

	/**
	 * Switches the page to be an XML, changing the head to an xml description
	 * @return this
	 */
	public PageBuilder SwitchToXML()
	{
		headBuilder.setLength(0);
		headBuilder.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
		headBuilder.append("<database xmlns=\"http://www.w3schools.com\" " +
							"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
							"xsi:schemaLocation=\"http://www.w3schools.com note.xsd\">\n");
		headClosed = true;
		
		bodyBuilder.setLength(0);
		bodyIndent = "\t";
		bodyClose = "</database>\n";
		
		return this;
	}
	
	public PageBuilder CloseHead() throws EOFException
	{
		CheckHeader();
		headBuilder.append("\t</head>\n");
		headClosed = true;
		return this;
	}

	public PageBuilder CloseBody() throws EOFException 
	{
		CheckBody();
		bodyBuilder.append(bodyClose);
		bodyClosed = true;
		return this;
	}

	public PageBuilder AddJavaScriptReference(String fileName) throws EOFException
	{
		CheckHeader();
		headBuilder.append("\t\t<script type=\"text/javascript\" src=\"../scripts/" +
						   fileName + "\"></script>\n");
		return this;
	}

	public PageBuilder AddCSSReference(String fileName) throws EOFException
	{
		CheckHeader();
		headBuilder.append("\t<link rel=\"stylesheet\" type=\"text/css\"" +
						   "href=\"../styles/"+ fileName + "\">");
		return this;
	}
	
	public PageBuilder AddTitle(String title) throws EOFException
	{
		CheckHeader();
		headBuilder.append("\t\t<title>" + title + "</title>\n");
		return this;
	}
	
	public PageBuilder AddHeaderTag(String tag) throws EOFException
	{
		CheckHeader();
		headBuilder.append("\t\t" + tag + "\n");
		return this;
	}
	
	/**
	 * Adds an element to the body at the first level (depth 2).<br>
	 * It is assumed that content has been formatted via <b>CreateDeepElement<b>
	 * @param element the enclosing tags
	 * @param content
	 * @throws EOFException
	 */
 	public PageBuilder AddBodyElement(String element, String content) throws EOFException
	{
		CheckBody();
		bodyBuilder.append(bodyIndent + "<" + element + ">\n" +
						   content +
						   bodyIndent + "</" + element + ">\n");
		return this;
	}
 	
	/**
	 * Creates a string with <b>content</b> contained inside <b>element</b> 
	 * tags indented by <b>depth</b> tabs
	 * Use inside <b>AddBodyELement</b>.
	 * If building HTML, start with depth = 3, if XML, start with 2
	 * @param depth
	 * @param element
	 * @param content
	 * @return a contained DOM element ready to enter into other elements
	 */
	static public String CreateDeepElement(int depth, String element, String content)
	{
		StringBuilder elementBuilder = new StringBuilder();
		StringBuilder depthBuilder = new StringBuilder();

		for (int index = 0; index < depth; index++)
		{
			depthBuilder.append("\t");
		}
		
		if (content.startsWith("\t"))
		{
			elementBuilder
				.append(depthBuilder).append("<" + element + ">\n")
					.append(content)
				.append(depthBuilder).append("</" + element + ">\n");
		}
		else
		{
			elementBuilder
				.append(depthBuilder).append("<" + element + ">")
					.append(content)
				.append("</" + element + ">\n");
		}
		
		return elementBuilder.toString();
	}

	/**
	 * Prepares the HttpServletResponse for sending, populating it with the page created.<br>
	 * After this completes successfully, the response may be sent.
	 * @param code the return code to enter into the response
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public boolean PrepareResponse(int code)
	{
		if (!headClosed || !bodyClosed)
		{
			return false;
		}
		StringBuilder entityBuilder = new StringBuilder().append(headBuilder).append(bodyBuilder);
		
		response.setStatus(code);
		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html");
		try
		{
			response.getWriter().write(entityBuilder.toString());
		} catch (IOException e)
		{
			System.err.println(e.getMessage());
			System.err.println("Problem with encoding HTML entity in PageBuilder.PrepareResponse.\n");
			e.printStackTrace();
		}
		
		return true;
	}

	public boolean PrepareXML()
	{
		if (!headClosed || !bodyClosed)
		{
			return false;
		}
		
		response.setCharacterEncoding("UTF-8");
		response.setContentType("application/force-download");//"application/vnd.google-earth.kml+xml");

		return true;
	}
	
	/**
	 * Generates an appropriate error page according to given parameters
	 * @param response where to write the error page to
	 * @param standardTitle the title of the error page
	 * @param standardHeading the top heading
	 * @param standardMessage a standard message
	 * @param givenMessage an additional possible message
	 * @param errorCode the error code to set for the response
	 */
	static public void GenerateErrorPage(HttpServletResponse response,
											 String standardTitle,
											 String standardHeading,
											 String standardMessage,
											 String givenMessage,
											 int errorCode)
	{
		PageBuilder errorPageBuilder = new PageBuilder(response);

		try
		{
			String messageString = givenMessage == null || givenMessage.isEmpty() ?
									"" : 
									PageBuilder.CreateDeepElement(3, "p", givenMessage);
			errorPageBuilder.AddTitle(standardTitle)
			.CloseHead().AddBodyElement("center",
					PageBuilder.CreateDeepElement(3, "h2", standardHeading) +
					messageString + 
					PageBuilder.CreateDeepElement(3, "p", standardMessage))
					.CloseBody().PrepareResponse(errorCode);
		} catch (EOFException e)
		{
			System.err.println(e.getMessage());
			e.printStackTrace();
		}
	}

	@Override
	public String toString()
	{
		return headBuilder.toString() + bodyBuilder.toString(); 
	}
}
